using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;

using ColladaDotNet.Pipeline.BSP.Quake3;

using Microsoft.Win32;

namespace RegisterShellExtensions
{
	[Guid("B641AF9A-A1FF-457C-BB6C-919D4100847C"), ComVisible(true)]
	public class ColladaDotNetShell
			: IShellExtInit
			  , IContextMenu
	{
		private const int S_FALSE = 1;
		private const int S_OK = 0;
		private const String THIS_GUID = @"B641AF9A-A1FF-457C-BB6C-919D4100847C";
		private const String THIS_NAME = "Collada.NET Converter";

		private const String XML_FILE_NAME = ".dae";

		protected System.Runtime.InteropServices.ComTypes.IDataObject m_dataObject;
		protected uint m_hDrop;

		public ColladaDotNetShell()
		{
		}

		#region IContextMenu

		private String dirPath;

		int IContextMenu.QueryContextMenu(HMenu hMenu, int iMenu, int idCmdFirst, int idCmdLast, CMF uFlags)
		{
			int id = 0;

			if ((uFlags & CMF.CMF_DEFAULTONLY) != 0)
				return 0;

			// Get the file name to work with
			var sb = new StringBuilder(1024);
			Helpers.DragQueryFile(m_hDrop, 0, sb, sb.Capacity + 1);
			dirPath = sb.ToString();

			if (!((uFlags & (CMF.CMF_VERBSONLY | CMF.CMF_NOVERBS)) == 0 || (uFlags & CMF.CMF_EXPLORE) != 0))
				return 0;

			//if (!(Directory.Exists(dirPath) || (File.Exists(dirPath) && dirPath.ToLower().EndsWith(".dae"))))
			//    return 0;

			if (File.Exists(dirPath) && dirPath.ToLower().EndsWith(".bsp"))
			{
				HMenu subMenu = Helpers.CreatePopupMenu();
				Helpers.InsertMenu(hMenu, iMenu,
				                   MFMENU.MF_STRING | MFMENU.MF_ENABLED, new IntPtr(idCmdFirst + (id++)), "Convert to COLLADA");
				return id;
			}

			
			return 0;
		}

		void IContextMenu.GetCommandString(int idCmd, uint uFlags, int pwReserved, StringBuilder commandString, int cchMax)
		{
			// GCS.VERB is the language indep command
			// GCS.HELPTEXT is the help :-)
			switch ((GCS)uFlags)
			{
				default:
					break;
			}
		}

		void IContextMenu.InvokeCommand(IntPtr pici)
		{
			try
			{
				var ici = (INVOKECOMMANDINFO)Marshal.PtrToStructure(pici, typeof(INVOKECOMMANDINFO));

				int id = ici.verb;

				if (File.Exists(dirPath) && dirPath.ToLower().EndsWith(".bsp"))
				{
					using (var s = File.OpenRead(dirPath))
					{
						BSPReader r = new BSPReader();
						var file = r.Parse(s);

						var c = file.GetColladaFileSolidMesh();

						c.Save(Path.ChangeExtension(dirPath,".dae"));
					}
				}
			}
			catch (Exception)
			{
			}
		}

		#endregion IContextMenu

		#region IShellExtInit

		int IShellExtInit.Initialize(IntPtr pidlFolder, IntPtr lpdobj, uint hKeyProgID)
		{
			try
			{
				// save the information about the selection
				m_dataObject = null;
				if (lpdobj != (IntPtr)0)
				{
					m_dataObject = (System.Runtime.InteropServices.ComTypes.IDataObject)Marshal.GetObjectForIUnknown(lpdobj);
					var fmt = new System.Runtime.InteropServices.ComTypes.FORMATETC();
					fmt.cfFormat = (short)CLIPFORMAT.CF_HDROP;
					fmt.ptd = IntPtr.Zero;
					fmt.dwAspect = System.Runtime.InteropServices.ComTypes.DVASPECT.DVASPECT_CONTENT;
					fmt.lindex = -1;
					fmt.tymed = System.Runtime.InteropServices.ComTypes.TYMED.TYMED_HGLOBAL;
					var medium = new System.Runtime.InteropServices.ComTypes.STGMEDIUM();
					m_dataObject.GetData(ref fmt, out medium);
					m_hDrop = (uint)medium.unionmember.ToInt32();
				}
			}
			catch (Exception)
			{
			}

			return 0;
		}

		#endregion IShellExtInit

		#region Registration

		private const int SHCNE_ASSOCCHANGED = 0x08000000;

		[DllImport("Shell32.dll")]
		private static extern void SHChangeNotify(int wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);

		private static void RegisterHandlerUsingKey_(RegistryKey rk)
		{
			using (rk)
			{
				RegistryKey rkTHIS = rk.OpenSubKey(THIS_NAME);
				if (rkTHIS == null)
					rkTHIS = rk.CreateSubKey(THIS_NAME);

				using (rkTHIS)
				{
					rkTHIS.SetValue("", "{" + THIS_GUID + "}");
				}
			}
		}

		[ComRegisterFunction]
		private static void RegisterServer(String str1)
		{
			try
			{
				RegisterHandlerUsingKey_(
						Registry.ClassesRoot.OpenSubKey(@"Directory\shellex\ContextMenuHandlers", true));
				RegisterHandlerUsingKey_(
						Registry.ClassesRoot.OpenSubKey(@"*\shellex\ContextMenuHandlers", true));

				// set as an approved shell extension so its use does
				// not require local admin rights
				using (RegistryKey rkApproved =
						Registry.LocalMachine.OpenSubKey(
								@"Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved", true))
				{
					rkApproved.SetValue("{" + THIS_GUID + "}", THIS_NAME);
				}

				// tell explorer to refresh
				SHChangeNotify(SHCNE_ASSOCCHANGED, 0, IntPtr.Zero, IntPtr.Zero);
			}
			catch (Exception e)
			{
				Console.WriteLine(e.ToString());
			}
		}

		[System.Runtime.InteropServices.ComUnregisterFunction]
		private static void UnregisterServer(String str1)
		{
			try
			{
				using (RegistryKey rk =
						Registry.ClassesRoot.OpenSubKey(@"Directory\shellex\ContextMenuHandlers", true))
				{
					rk.DeleteSubKey(THIS_NAME, false);
				}
				using (RegistryKey rk =
						Registry.ClassesRoot.OpenSubKey(@"*\shellex\ContextMenuHandlers", true))
				{
					rk.DeleteSubKey(THIS_NAME, false);
				}

				// set as an approved shell extension so its use does
				// not require local admin rights
				using (RegistryKey rkApproved =
						Registry.LocalMachine.OpenSubKey(
								@"Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved", true))
				{
					rkApproved.DeleteValue("{" + THIS_GUID + "}", false);
				}

				// tell explorer to refresh
				SHChangeNotify(SHCNE_ASSOCCHANGED, 0, IntPtr.Zero, IntPtr.Zero);
				Console.WriteLine(THIS_NAME + " un-registered successfuly");
			}
			catch (Exception e)
			{
				Console.WriteLine(e.ToString());
			}
		}

		public static void Register()
		{
			Assembly asm = Assembly.GetExecutingAssembly(); // Get the assembly in which I can be found
			var reg = new RegistrationServices();
			if (reg.RegisterAssembly(asm, AssemblyRegistrationFlags.SetCodeBase))
			{
				Console.WriteLine(asm.FullName + " registered");
			}
			else
			{
				Console.WriteLine("Error: " + asm.FullName + " was not registered");
			}
		}

		public static void UnRegister()
		{
			Assembly asm = Assembly.GetExecutingAssembly(); // Get the assembly in which I can be found
			var reg = new System.Runtime.InteropServices.RegistrationServices();
			if (reg.UnregisterAssembly(asm))
			{
				Console.WriteLine(asm.FullName + " unregistered");
			}
			else
			{
				Console.WriteLine("Error: " + asm.FullName + " was not unregistered");
			}
		}

		#endregion Registration
	}
}